/**
 * Copyright: Public Domain
 * 
 * Authors:
 *	Walter Bright, Digital Mars, www.digitalmars.com
 *	Tomasz Stachowiak (isStaticArray, isExpressionTuple)
 *  Wei Li (oldrev@gmail.com)
 */

module dotmars.base.typetraits;

/***
 * Get the type of the return value from a function,
 * a pointer to function, or a delegate.
 * Example:
 * ---
 * import std.traits;
 * int foo();
 * ReturnType!(foo) x;   // x is declared as int
 * ---
 */
template ReturnType(alias dg)
{
    alias ReturnType!(typeof(dg)) ReturnType;
}

/** ditto */
template ReturnType(dg)
{
    static if (is(dg R == return))
	alias R ReturnType;
    else
	static assert(0, "argument has no return type");
}

/***
 * Get the types of the paramters to a function,
 * a pointer to function, or a delegate as a tuple.
 * Example:
 * ---
 * import std.traits;
 * int foo(int, long);
 * void bar(ParameterTypeTuple!(foo));      // declares void bar(int, long);
 * void abc(ParameterTypeTuple!(foo)[1]);   // declares void abc(long);
 * ---
 */
template ParameterTypeTuple(alias dg)
{
    alias ParameterTypeTuple!(typeof(dg)) ParameterTypeTuple;
}

/** ditto */
template ParameterTypeTuple(dg)
{
    static if (is(dg P == function))
	alias P ParameterTypeTuple;
    else static if (is(dg P == delegate))
	alias ParameterTypeTuple!(P) ParameterTypeTuple;
    else static if (is(dg P == P*))
	alias ParameterTypeTuple!(P) ParameterTypeTuple;
    else
	static assert(0, "argument has no parameters");
}


/***
 * Get the types of the fields of a struct or class.
 * This consists of the fields that take up memory space,
 * excluding the hidden fields like the virtual function
 * table pointer.
 */

template FieldTypeTuple(S)
{
    static if (is(S == struct) || is(S == class))
	alias typeof(S.tupleof) FieldTypeTuple;
    else
	static assert(0, "argument is not struct or class");
}


/***
 * Get a TypeTuple of the base class and base interfaces of
 * this class or interface.
 * Example:
 * ---
 * import std.traits, std.typetuple, std.stdio;
 * interface I { }
 * class A { }
 * class B : A, I { }
 *
 * void main()
 * {
 *     alias BaseTypeTuple!(B) TL;
 *     writefln(typeid(TL));	// prints: (A,I)
 * }
 * ---
 */

template BaseTypeTuple(A)
{
    static if (is(A P == super))
	alias P BaseTypeTuple;
    else
	static assert(0, "argument is not a class or interface");
}

debug(Unittest) unittest
{
    interface I { }
    class A { }
    class B : A, I { }

    alias BaseTypeTuple!(B) TL;
    assert(TL.length == 2);
    assert(is (TL[0] == A));
    assert(is (TL[1] == I));
}

/* *******************************************
 */

template IsStaticArrayType(T)
{
	const bool IsStaticArrayType = is( typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T );
}
 	

debug(Unittest) unittest
{
	static assert (IsStaticArrayType!(int[51]));
	static assert (IsStaticArrayType!(int[][2]));
	static assert (IsStaticArrayType!(char[][int][11]));
	static assert (!IsStaticArrayType!(int[]));
	static assert (!IsStaticArrayType!(int[char]));
	static assert (!IsStaticArrayType!(int[1][]));
}

/**
 * Tells whether the tuple T is an expression tuple.
 */
template IsExpressionTuple(T ...)
{
    static if (is(void function(T)))
	const bool IsExpressionTuple = false;
    else
	const bool IsExpressionTuple = true;
}



/**
  * 
  */
template IsIntegerType(T)
{
	static if( is(T == byte) || is(T == ubyte) || 
			is(T == short) || is(T == ushort) ||
			is(T == int) || is(T == uint) ||
			is(T == long) || is(T == ulong)) 
			/*is(T == cent) || is(T == ucent) //reserved for future*/
	{
		const bool IsIntegerType = true;	
	}
	else
	{
		const bool IsIntegerType = false;
	}
}


template IsCharType(T)
{
	static if( is(T == char) || is(T == wchar) || is(T == dchar))
		const bool IsCharType = true;
	else
		const bool IsCharType = false;
}


template IsFloatType(T)
{
	static if( is(T == float) || is(T == double) || is(T == real))
		const bool IsFloatType = true;
	else
		const bool IsFloatType = false;
}


template IsComplexNumberType(T)
{
	static if( is(T == cfloat) || is(T == cdouble) || is(T == creal))
		const bool IsComplexNumberType = true;
	else 
		const bool IsComplexNumberType = false;
}


template IsImaginaryType(T)
{
	static if( is(T == ifloat) || is(T == idouble) || is(T == ireal))
		const bool IsImaginaryType = true;
	else 
		const bool IsImaginaryType = false;
}


template IsVoidType(T)
{
	static if( is(T == void))
		const bool IsVoidType = true;
	else 
		const bool IsVoidType = false;
}

template IsPrimitiveType(T)
{
	static if( is(T == void) || is(T == bool) ||
			is(T == byte) || is(T == ubyte) || is(T == short) || is(T ==ushort) ||
			is(T == int) || is(T == uint) || is(T == long) || is(T == ulong) ||
			/*is(T == cent) || is(T == ucent) || */
			is(T == float) || is(T == double) || is(T == real) ||
			is(T == ifloat) || is(T == idouble) || is(T == ireal) ||
			is(T == cfloat) || is(T == cdouble) || is(T == creal) ||
			is(T == char) || is(T == wchar) || is(T == dchar))
		const bool IsPrimitiveType = true;
	else
		const bool IsPrimitiveType = false;
}


/**
  *
  *
  */
private template IsPointerDisp(T) {
	const bool IsPointerDisp = false;
}

private template IsPointerDisp(T : T*) {
	const bool IsPointerDisp = true;
}

template IsPointerType(T)
{
	const bool IsPointerType = IsPointerDisp!(T);
}

static assert(IsPointerType!(char*));
static assert(!IsPointerType!(char));


private template IsDynamicArrayDisp(T) {
	const bool IsDynamicArrayDisp = false;
}

private template IsDynamicArrayDisp(T : T[]) {
	const bool IsDynamicArrayDisp = true;
}

/**
  *
  *
  */
public template IsDynamicArrayType(T)
{
	const bool IsDynamicArrayType = IsDynamicArrayDisp!(T);
}


template IsAssocArray(T) {
	const bool IsAssocArray = is( typeof(T.init.values[0])[typeof(T.init.keys[0])] == T );
}


public template ElementTypeOf(T)
{
	static if(IsAssocArray!(T))
		alias typeof(T.init.values[0]) ElementTypeOf;
	else
		alias typeof(T[0]) ElementTypeOf;
}


template IndexTypeOf(T) {
	static if(IsAssocArray!(T))
		alias typeof(T.init.keys[0]) IndexTypeOf;
	else 
		alias size_t IndexTypeOf; 
}


template HasMember(T, string member)  
{  
    const bool HasMember = is(typeof(mixin("T." ~ member)));  
}  

template HasMember(alias T, string member)  
{  
    const bool HasMember = is(typeof(mixin("T." ~ member)));  
} 

